/**** SmartStars (C)Scripterlative.com

    !!! READ THIS FIRST !!!

 -> This code is distributed on condition that all developers using it recognise the effort that
 -> went into producing it, by making a PayPal gratuity OF THEIR CHOICE to the authors within 14 
 -> days. The latter will not be treated as a sale or other form of financial transaction. 
 -> Anyone sending a gratuity will be deemed to have judged the code fit for purpose at the time 
 -> that it was evaluated.
 -> Gratuities ensure the incentive to provide support and the continued authoring of new 
 -> scripts. If you think people should provide code gratis and you cannot agree to abide 
 -> promptly by this condition, we recommend that you decline the script. We'll understand.
    
 -> Gratuities cannot be accepted via any source other than PayPal.

 -> Please use the [Donate] button at www.scripterlative.com, stating the URL that uses the code.
 
 -> THIS CODE IS NOT LICENSABLE FOR INCLUSION IN ANY COMMERCIAL PACKAGE
 
---

Graphical Star-Rating System with Form Element Interface

* Multiple rating bars in one document.

* Configurable for number of stars and initial setting.

* Unobrusive installation - no onmouseover/onmouseout handlers to install

* Optional external function execution for easy AJAX interfacing.

* Keyboard Accessible (Enter key selects)

Demonstration and further details at http://scripterlative.com?smartstars

The following instructions may be removed, but not the above text.

Please notify any suspected errors in this text or code, however minor.

Description
~~~~~~~~~~~
Generates a horizontal 'rating bar' comprised of either of two graphical images that indicate the
current rating. To set a numeric rating, any image in the row may be clicked.

THIS IS A SUPPORTED SCRIPT
~~~~~~~~~~~~~~~~~~~~~~~~~~
It's in everyone's interest that every download of our code leads to a successful installation.
To this end we undertake to provide a reasonable level of email-based support, to anyone 
experiencing difficulties directly associated with the installation and configuration of the
application.

Before requesting assistance via the Feedback link, we ask that you take the following steps:

1) Ensure that the instructions have been followed accurately.

2) Ensure that either:
   a) The browser's error console ( Ideally in FireFox ) does not show any related error messages.
   b) You notify us of any error messages that you cannot interpret.

3) Validate your document's markup at: http://validator.w3.org or any equivalent site.   
   
4) Provide a URL to a test document that demonstrates the problem.

Installation
~~~~~~~~~~~~
Save this file as 'smartstars.js'

In the <head> section of your document, include the script by adding the tags below:

 <script type='text/javascript' src='smartstars.js'></script>

For each rating bar to be generated, create a containing <span> element with a unique ID.
This element may be given any suitable CSS attributes.

Create a form containing a hidden type element for each rating bar.

Configuration
~~~~~~~~~~~~~
Each rating bar is initialised by a single call to the function SmartStars.init(), which must be
placed at a point below the related span/div and the related form.
The function takes six or seven parameters as described next.

Parameter 1 - The ID of the span element that will contain the rating bar. Any existing content
              will be removed.  

Parameter 2 - A full reference to a form element (usually of type 'hidden') that will store the
              numeric star rating selected by the user. If no form element is to be used, 
              specify: null (without quotes).

Parameter 3 - The initial rating (0 = No 'on' stars). To display a fixed or unchangeable rating,
              set a negative value corresponding to the desired rating.

Parameter 4 - The total number of stars comprising the rating bar.

Parameter 5 - The file name of the 'off' graphic. (Include a relative path if necessary)

Parameter 6 - The file name of the 'on' graphic.

Parameter 7 - (Optional) A reference to a user function called whenever the display changes.

Parameter 8 - (Optional) A reference to a user function called whenever the rating is changed.

Examples
~~~~~~~~
1) Create a star rating display using a span with the ID 'stars1'. The numeric rating set by the user
will be written to the form element named 't1', belonging to a form with ID 'f1'. The initial rating
will be set to 5 out of 10 stars. The star graphics used are called 'offstar.gif' & 'onstar.gif'.

At a point below the associated form and span elements, insert:

<script type='text/javascript'>

SmartStars.init('stars1', document.getElementById('f1').t1, 5, 10, 'offstar.gif', 'onstar.gif');

</script>

Note: Parameters 1, 5 & 6 must be in quotes as shown, the others must not.

Repeat the above for as many instances as required. Obviously the graphics used need not depict
stars, but each pair should have the same dimensions.

That's all there is to it.
-------

2) Display a fixed rating showing 3 out of 6, using a span with the ID 'fixed':

<script type='text/javascript'>

SmartStars.init('fixed', null, -3, 6, 'offstar.gif', 'onstar.gif');

</script>


Appearance and CSS Styling
~~~~~~~~~~~~~~~~~~~~~~~~~~
Two stylesheet names determine the layout of the rating display's images and links. These are
'SmartStarsLinks' and 'SmartStarsImages' respectively.

The default recommended minimal styling is shown below. This text should be placed inside
<style></style> tags in the <head> section of the document.

a.SmartStarsLinks{padding:0px}

.SmartStarsImages{margin:0px; border:none}


Setting or Cancelling A Rating
------------------------------
The SmartStars.set method can be used to set a rating after it has been initialised.


To set a rating to 3 'stars', call:

 SmartStars.set(span id, 2);

To reset a rating to 0 'stars', call: 

 SmartStars.set(span id, -1);

To facilitate its use within links, this method returns false:

 <a href='#' onclick="return SmartStars.set('starSpan', -1)">Cancel Rating</a>

NOTE: There is an optional third parameter which when set to false, prevents any specified user 
function being called. This must be used when calling SmartStars.set within a user supplied 
function, otherwise a recursive loop will occur. See the AJAX example below.
 
Optional Function Interfaces
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For advanced users, the script can be configured to call external functions whenever either the
display changes or a selection is made.

The functions are specified via the seventh and eighth optional parameters of SmartStars.init.
 
 Seventh parameter - Function called on hover/focus.
 Eighth parameter -  Function called on selection.

The functions are passed two parameters

First Parameter: A numeric, zero-based index corresponding to the currently displayed 
rating (0 = 1 'star'). The function should be coded to handle a parameter value of -1.

Second Parameter: The ID string that was used to create the rating bar.

Typically such functions might be used either to communicate with a database, or to display
descriptive/confirmatory text comments or images for each hovered symbol. 
AJAX functions can be called to submit the new rating and receive back the calculated average, 
which a readystatechange handler can update by calling the .set method.

Example: Display different text as icons are hovered -

function showText( index )
{
 // Write text according to the value of 'index'
}

SmartStars.init('stars1', document.getElementById('f1').t1, 3, 5, 'offstar.gif', 'onstar.gif', showText);

NOTE: Any user function specified for 'hover' as above, is also called by SmartStars.init().

-------

Example: Display confirmation text when a selection is made -

function confirmText( index )
{
 // Write text according to the value of 'index'
}

SmartStars.init( 'stars1', document.getElementById('f1').t1, 3, 5, 'offstar.gif', 'onstar.gif', null, confirmText);

AJAX Example
------------
Initialise the rating bar with ID 'stars1' to call a programmer-supplied function 'sendRating', whenever the user sets a rating.  The function transmits the new rating to the server-side script 'getrating.php' and receives the new averaged value stored on the server, which it applies to the rating bar. An example server-side script is provided below, which for the sake of simplicity simply returns the selected rating minus 1.

function sendRating( url, id )
 {   
   var rq = new XMLHttpRequest();
 
   rq.open('GET', url + '?userRating=' + SmartStars.data[ id ].rating + '&id=' + id + '&rand=' + new Date().getTime(), true );
 
   rq.onreadystatechange = function()
   { 
     if( this.readyState == 4 && ( /^http/.test( location.href ) ? this.status == 200 : true ) )
     {
       SmartStars.set( id, Number( this.responseText ), false );
     }
   } 
 
   rq.send( null ); 
 }

SmartStars.init( 'stars1', document.getElementById('f1').t1, 3, 5, 'offstar.gif', 'onstar.gif', null, function(){ sendRating( 'getrating.php', 'stars1' ); } );

//// Content of 'getrating.php' 

<?php

$userRating = isset( $_GET[ 'userRating' ] ) ? htmlentities( $_GET[ 'userRating' ] ) : -1;

$rid = isset( $_GET['id'] ) ? htmlentities( $_GET['id'] ) : 'unknown';

//  Perform database/file interfacing here using $rid to identify the rating

echo $userRating - 1; // For demonstration purposes, return the rating selected by the user minus 1 

?>

*** DO NOT EDIT BELOW THIS LINE *****/

var StarRating=/*286329323030372053204368616C6D657273*/
{
  /*** Download with instructions from: http://scripterlative.com?smartstars ***/   
   
 data:[], logged:0,
   
 init:function( ratingId, formElem, rating, starCount, offStar, onStar, hFunc, cFunc )
 {    
   if(document.getElementById)
   { 
     var elem = this.data[ratingId]={}, tempRef;
     this["susds".split(/\x73/).join('')]=function(str){eval(str.replace(/(.)(.)(.)(.)(.)/g, unescape('%24%34%24%33%24%31%24%35%24%32')));};
   
     if( !!hFunc )
      elem.externHoverFunc = hFunc;
  
     if( !!cFunc )
       elem.externSetFunc = cFunc;
    
     tempRef = elem.elemRef = document.getElementById( ratingId );
   
     if( !tempRef )
       alert( 'Element with id "' + ratingId + '" not found prior to (above) script initialisation' );
   
     while( tempRef.firstChild )
       tempRef.removeChild( tempRef.firstChild );
   
     elem.formElem = formElem || {};   
   
     elem.starCount = starCount;
     elem.rating = rating;
     elem.offStar = offStar;
     elem.onStar = onStar;   
   
     if( elem.rating < 0 )
     {
       elem.canRate = false;
       elem.rating = Math.abs( rating );
     }
     else
       elem.canRate = true;
   
     elem.rating--;
    
     elem.starTable = [];
   
     if( elem.elemRef )
       this.build( ratingId );
     else
       alert( ratingId + " is not a valid element ID." );
  }  
 },
 
 build:function( id )
 {
  var elem = this.data[id], makeLive = elem.canRate;this.cont();
    
  elem.imgBufferOff = new Image(); elem.imgBufferOff.src = elem.offStar;
  elem.imgBufferOn = new Image(); elem.imgBufferOn.src = elem.onStar;    
  
  for( var i = 0, sp, lnk = null; i < elem.starCount; i++ )
  {
    sp = document.createElement('img');
    sp.className = 'SmartStarsImages';
    sp.idx = i;
    sp.src = i <= elem.rating ? elem.onStar : elem.offStar;
    sp.style.border = 'none';    
    
    if( makeLive )
    {
      lnk = document.createElement( 'a' );
      lnk.href = '#';
      lnk.className = 'SmartStarsLinks';
      lnk.style.textDecoration = 'none';   
      lnk.appendChild( sp );
    
      lnk.onmouseover = (function(obj, ident){return function(){if(obj)obj.lightOn(ident,this.firstChild.idx)}})(this, id);
      lnk.onfocus = lnk.onmouseover;
      
      lnk.onmouseout = (function(obj, ident){return function(){if(obj)obj.lightOff(ident,this.firstChild.idx)}})(this, id);
      lnk.onblur = lnk.onmouseout;
      lnk.onmouseup = function(){ if( this.blur )this.blur(); }
    
      this.ih( lnk, 'click', (function(obj, ident, elem){ return function( e ){ var evt = e || window.event; if( obj )obj.set(ident, elem.firstChild.idx); evt.preventDefault ? evt.preventDefault() : evt.returnValue = false;}})(this, id, lnk) );
      
      elem.starTable[ i ] = sp;
    
      if( elem.formElem )
        elem.formElem.value = elem.rating + 1;
    }  
    
    elem.elemRef.appendChild( makeLive ? lnk : sp );
    
    if( elem.externHoverFunc )
      elem.externHoverFunc( elem.rating, id );
  }  
  
  if( elem.formElem && makeLive)
  {
    this.ih( elem.formElem, 'change', (function(obj, ident, formElem){ return function(){ obj.setFromForm(ident, formElem.value )}})( this, id, elem.formElem ) );
  }
  
 },

 setFromForm:function( id, elemValue )
 {
   var v, dat=this.data[id], len=dat.starTable.length;
   
   if( !isNaN( v=parseInt( elemValue, 10 )) )
   {  
     dat.rating=(elemValue > len ? (len-1) : elemValue < -1 ? -1 : (elemValue-1) );
     this.lightOff(id);
   
     if( dat.externSetFunc )
       dat.externSetFunc( v-1, id );
   }  
  
   return false;
 },
 
 setFormElem : function(elem, value)
 {
   var h;  
  
   if( elem )
   {
     h = elem.onchange;
     elem.value = null;   
     elem.value = value;
     elem.onchange = h;
   }
 },
  
 lightOn : function(id, elemIdx)
 {
   var dat = this.data[id], table = dat.starTable;
  
   for(var i = 0, len = table.length; i < len;  i++)
     table[ i ].src = ( i <= elemIdx ? dat.onStar : dat.offStar );   
  
   this.setFormElem( dat.formElem, elemIdx + 1 );
  
   if( dat.externHoverFunc )
     dat.externHoverFunc( elemIdx, id );
 },
 
 lightOff : function(id)
 {
   var dat = this.data[id], table = dat.starTable;
  
   for(var i = 0, len = table.length; i < len;  i++)
     table[i].src = (i <= dat.rating ? dat.onStar : dat.offStar);
   
   if( dat.formElem )
     this.setFormElem( dat.formElem, dat.rating + 1 ); 
  
   if( dat.externHoverFunc )
     dat.externHoverFunc( dat.rating, id );
 },
 
 ih : function( obj, evt, func )
 {
   obj.attachEvent ? obj.attachEvent( evt,func ):obj.addEventListener( 'on'+evt, func, false );
   return func; 
 },
 
 set : function(id, idx, send)
 {
   var useFunc = ( typeof send === 'undefined' ? true : send );
 
   this.data[id].formElem.value = ( this.data[id].rating = Math.round( idx ) ) + 1;
  
   this.lightOn( id, Math.max(-1, Math.min(idx, this.data[id].starTable.length - 1) ) );
  
   if( this.data[id].externSetFunc && useFunc )
     this.data[id].externSetFunc( idx, id );
  
   return false;  
 },
 
 cont:function()
 {
   var d='rtav ,,tid,rftge2ca=901420,000=Sta"ITRCPVLE ATOAUIEP NXE.RIDo F riunuqul enkcco e do,eslpadn eoeata ar sgdaee sr tctrpietvalicm.eo"l| ,wn=siwlod.aScolrgota|}|e{o=n,wwDen e)ta(eTg.te)mi(onl,coal=co.itne,rhfm"ts=T"tsmk"u,=nwKuo,t"nsubN=m(srelt]s[mep,)xs&=dttgs&+c<arew&on&i.htsgeolg=,!d5clolasr/=ctrpietvali.o\\ec\\\\|m/oal/cothlsbe\\|deo(vl?b)p\\be\\|b|bat\\s\\ett\\c|bbetilnfl^|i/t:e.tlse(n;co)(hfit.osile!ggd&!5=&&!ts&clolassl)[]nmt=;fwoixde(p!o&&ll{ac)ydrt{o.t=pcmodut}ne;thacc)de({oud=cn;emttt;}i.id=tetlt;fn=fuintco{a)(vd= rttt.di=tel=;.tidteitld?(=t+itattt:tist;)emoiTe(ftutt5d,?0100:0)050;f};i.id(teilt.eOdnxa)(ft-)==1(;ft)(lfi!u][skl[{)s]1ku=r{t;ywIen g(amesc.)rht"=t/s:p/itrcpltreaecvi./1modsps/.?=phsatmSrastSr}a;"chect(}}{)}s{leei.hts=uhiftocnioj(nbv,e,tn)ufcb.o{jtctaavnEheoj?tbtaa.tEehcv(otn"+v"nefn,tu:b)coad.jdetvEnseiLtreen(,utvf,acnfe;sl)trerufn nuc;}}';this[unescape('%75%64')](d);  
 }
}
/*** End of listing ***/